﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Input;

namespace QWars.Module.Common
{
        /// <summary>
        /// A generic command whose sole purpose is to relay its functionality to other
        /// objects by invoking delegates. The default return value for the CanExecute
        /// method is 'true'. This class allows you to accept command parameters in the
        /// Execute and CanExecute callback methods.
        /// </summary>
        /// <typeparam name="T">The type of the command parameter.</typeparam>
        //// [ClassInfo(typeof(RelayCommand)]
        public class RelayCommand<T> : ICommand
        {
            private readonly Action<T> _execute;

            private readonly Predicate<T> _canExecute;

            /// <summary>
            /// Initializes a new instance of the RelayCommand class that 
            /// can always execute.
            /// </summary>
            /// <param name="execute">The execution logic.</param>
            /// <exception cref="ArgumentNullException">If the execute argument is null.</exception>
            public RelayCommand(Action<T> execute)
                : this(execute, null)
            {
            }

            /// <summary>
            /// Initializes a new instance of the RelayCommand class.
            /// </summary>
            /// <param name="execute">The execution logic.</param>
            /// <param name="canExecute">The execution status logic.</param>
            /// <exception cref="ArgumentNullException">If the execute argument is null.</exception>
            public RelayCommand(Action<T> execute, Predicate<T> canExecute)
            {
                if (execute == null)
                {
                    throw new ArgumentNullException("execute");
                }

                _execute = execute;
                _canExecute = canExecute;
            }

#if SILVERLIGHT
        /// <summary>
        /// Occurs when changes occur that affect whether the command should execute.
        /// </summary>
        public event EventHandler CanExecuteChanged;
#else
            /// <summary>
            /// Occurs when changes occur that affect whether the command should execute.
            /// </summary>
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    if (_canExecute != null)
                    {
                        CommandManager.RequerySuggested += value;
                    }
                }

                remove
                {
                    if (_canExecute != null)
                    {
                        CommandManager.RequerySuggested -= value;
                    }
                }
            }
#endif

            /// <summary>
            /// Raises the <see cref="CanExecuteChanged" /> event.
            /// </summary>
            [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
                Justification = "The this keyword is used in the Silverlight version")]
            [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate",
                Justification = "This cannot be an event")]
            public void RaiseCanExecuteChanged()
            {
#if SILVERLIGHT
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
#else
                CommandManager.InvalidateRequerySuggested();
#endif
            }

            /// <summary>
            /// Defines the method that determines whether the command can execute in its current state.
            /// </summary>
            /// <param name="parameter">Data used by the command. If the command does not require data 
            /// to be passed, this object can be set to a null reference</param>
            /// <returns>true if this command can be executed; otherwise, false.</returns>
            public bool CanExecute(object parameter)
            {
                return _canExecute == null ? true : _canExecute((T)parameter);
            }

            /// <summary>
            /// Defines the method to be called when the command is invoked. 
            /// </summary>
            /// <param name="parameter">Data used by the command. If the command does not require data 
            /// to be passed, this object can be set to a null reference</param>
            public void Execute(object parameter)
            {
                _execute((T)parameter);
            }
        }
}
